package com.example.tool.util;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dingtalkcontact_1_0.models.ListEmpLeaveRecordsHeaders;
import com.aliyun.dingtalkcontact_1_0.models.ListEmpLeaveRecordsRequest;
import com.aliyun.dingtalkcontact_1_0.models.ListEmpLeaveRecordsResponse;
import com.aliyun.dingtalkcontact_1_0.models.ListEmpLeaveRecordsResponseBody;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.dingtalk.api.request.OapiV2UserGetRequest;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiRobotSendResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.example.tool.bean.ddaiexcel.AddAiSheetRecordsRequest;
import com.example.tool.bean.ddaiexcel.AddAiSheetRecordsResponse;
import com.example.tool.bean.ddaiexcel.AiSheetFieldListResponse;
import com.example.tool.bean.ddaiexcel.AiSheetListResponse;
import com.example.tool.bean.ddaiexcel.CreateAiSheetFieldRequest;
import com.example.tool.bean.ddaiexcel.CreateAiSheetFieldResponse;
import com.example.tool.bean.ddaiexcel.CreateAiSheetRequest;
import com.example.tool.bean.ddaiexcel.CreateAiSheetResponse;
import com.example.tool.bean.ddaiexcel.CreateDocRequest;
import com.example.tool.bean.ddaiexcel.CreateDocResponse;
import com.example.tool.bean.ddaiexcel.DDFieldType;
import com.example.tool.bean.ddaiexcel.DeleteAiSheetFieldResponse;
import com.example.tool.bean.ddaiexcel.DeleteAiSheetRecordsRequest;
import com.example.tool.bean.ddaiexcel.DeleteAiSheetRecordsResponse;
import com.example.tool.bean.ddaiexcel.GetRecordsResponse;
import com.example.tool.bean.ddaiexcel.GetSheetResponse;
import com.example.tool.bean.ddaiexcel.ListRecordsRequest;
import com.example.tool.bean.ddaiexcel.ListRecordsResponse;
import com.example.tool.bean.ddaiexcel.UpdateAiSheetFieldRequest;
import com.example.tool.bean.ddaiexcel.UpdateAiSheetFieldResponse;
import com.example.tool.bean.ddaiexcel.UpdateAiSheetRecordsRequest;
import com.example.tool.bean.ddaiexcel.UpdateAiSheetRecordsResponse;
import com.example.tool.bean.ddaiexcel.WorkspaceListResponse;
import com.example.tool.service.RedisService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.taobao.api.ApiException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

//@EnableRetry
@Slf4j
@Component
public class DDUtil {

    @Getter
    private static RedisService redisService;
    private static RetryTemplate retryTemplate;
    private static final String ddAccessTokenKey = "sys:ddAccessToken:key";
    private static final String ddApiQueKey = "sys:dingDingApi:ddApiQue";
    private static final String appKey = "dingt9jxauvqgbtn2ezq";
    private static final String appSecret = "1upRScBqCu7D5472kr6OEQMCu0gPDtY6ms-OC7INdDe-zJm4Y2RJRSSZNRz7N3hM";
    private static final String operatorId = "UdqiP6JZuiPPqqWv9na5Jt4wiEiE";
    public static final Integer sleepTime = 2;
    public static final Integer consecutiveReqNum = 3;
    private static final String BASE_URL = "https://oapi.dingtalk.com";
    private static final String ddLinkUrl = "https://alidocs.dingtalk.com/i/nodes/%s";
    private static final String tokenKey = "x-acs-dingtalk-access-token";
    private static final String docId = "ZgpG2NdyVXaOEALjTqLo5QkXJMwvDqPk";

    @Autowired
    public void setRetryTemplate(RetryTemplate retryTemplate) {
        DDUtil.retryTemplate = retryTemplate;
    }


    @Autowired
    public void setRedisService(RedisService redisService) {
        DDUtil.redisService = redisService;
    }

    public static RestTemplate getRestTemplate() {
        return new RestTemplate();
    }


    public static String getToken() {
        try {
            String accessToken = "";
            Boolean hasKey = getRedisService().hasKey(ddAccessTokenKey);
            if (!hasKey) {
                String url = java.lang.String.format("%s/gettoken", BASE_URL);
                DingTalkClient client = new DefaultDingTalkClient(url);
                OapiGettokenRequest req = new OapiGettokenRequest();
                req.setAppkey(appKey);
                req.setAppsecret(appSecret);
                req.setHttpMethod("GET");
                OapiGettokenResponse rsp = client.execute(req);
                JSONObject result = JSONObject.parseObject(rsp.getBody());
                Integer errCode = result.getInteger("errcode");
                Integer expiresIn = result.getInteger("expires_in") - 1000;
                accessToken = result.getString("access_token");
                getRedisService().setWithExpire(ddAccessTokenKey, accessToken, expiresIn, TimeUnit.SECONDS);
            } else {
                accessToken = getRedisService().getString(ddAccessTokenKey);
            }
            return accessToken;
        } catch (ApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 读取工作表列表
     * @param workbookId
     * @param sheetId
     * @param rangeAddress
     * @return
     * @throws Exception
     */

    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static String readSheets(String workbookId, String sheetId, String rangeAddress) {

//        return retryTemplate.execute(context -> {
        String url = null;
        try {
            url = java.lang.String.format(
//                            "%s/v1.0/doc/workbooks/%s/sheets/%s/ranges/%s?select=values&operatorId=%s",

                    "https://api.dingtalk.com/v1.0/doc/workbooks/%s/sheets/%s/ranges/%s?select=values&operatorId=%s",
                    URLEncoder.encode(workbookId, "UTF-8"),
                    URLEncoder.encode(sheetId, "UTF-8"),
                    URLEncoder.encode(rangeAddress, "UTF-8"),
                    URLEncoder.encode(operatorId, "UTF-8")
            );
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        // 创建 RestTemplate
        RestTemplate restTemplate = getRestTemplate();
        // 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("x-acs-dingtalk-access-token", getToken());
        HttpEntity<String> entity = new HttpEntity<>(headers);
        System.out.println("workbookId:" + workbookId + " sheetId:" + sheetId + " rangeAddress:" + rangeAddress);
        // 发送 GET 请求
        ResponseEntity<String> response = restTemplate.exchange(
                URI.create(url),
                HttpMethod.GET,
                entity,
                String.class
        );

        return response.getBody();
//        });
    }


    /**
     * 获取工作表
     * @param workbookId
     * @return
     */
    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static JSONObject getSheets(String workbookId) {
        return retryTemplate.execute(context -> {
            try {
                RestTemplate restTemplate = new RestTemplate();
                restTemplate.getMessageConverters().clear();
                restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
                HttpHeaders headers = new HttpHeaders();
                headers.set("x-acs-dingtalk-access-token", getToken());
                String url = "https://api.dingtalk.com/v1.0/doc/workbooks/{workbookId}/sheets?operatorId={operatorId}";

                ResponseEntity<String> response = restTemplate.exchange(
                        url,
                        HttpMethod.GET,
                        new HttpEntity<>(headers),
                        String.class,
                        workbookId,
                        operatorId
                );
                lockApiQue();
//            try {
//                // 休眠3秒（可读性更好）
//                TimeUnit.SECONDS.sleep(sleepTime);
//            } catch (InterruptedException ignored) {}
                return JSONObject.parseObject(response.getBody());
            } catch (HttpClientErrorException e) {
                if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
                    throw new RuntimeException("Invalid workbook ID or request parameters");
                }
                throw e;
            } catch (Exception e) {
                throw new RuntimeException("Failed to retrieve sheets", e);
            }
        });
    }

    /**
     * 更新区域
     * @param workbookId
     * @param sheetId
     * @param rangeAddress
     * @param values
     */
    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static String updateRange(String workbookId, String sheetId, String rangeAddress, List<List<String>> values, List<List<String>> backgroundColors) {
        return retryTemplate.execute(context -> {
            String url = java.lang.String.format("https://api.dingtalk.com/v1.0/doc/workbooks/%s/sheets/%s/ranges/%s?operatorId=%s",
                    workbookId, sheetId, rangeAddress, operatorId);
            // 构建请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("x-acs-dingtalk-access-token", getToken());
            JSONObject requestBody = new JSONObject();
            requestBody.put("values", values);
            if (ObjectUtil.isNotNull(backgroundColors) && backgroundColors.size() == values.size()) {
                requestBody.put("backgroundColors", backgroundColors);
            }
            HttpEntity<String> requestEntity = new HttpEntity<>(requestBody.toJSONString(), headers);
            RestTemplate restTemplate = new RestTemplate();

            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, String.class);
            lockApiQue();
//            try {
//                // 休眠3秒（可读性更好）
//                TimeUnit.SECONDS.sleep(sleepTime);
//            } catch (InterruptedException ignored) {}
            System.out.println("Response Status: " + response.getStatusCode());
            System.out.println("Response Body: " + response.getBody());
            return response.getBody();
        });
    }

    /**
     * 更新区域
     * @param workbookId
     * @param sheetId
     * @param rangeAddress
     * @param values
     */
    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static String updateRange(String workbookId, String sheetId, String rangeAddress, List<List<String>> values) {
        return updateRange(workbookId, sheetId, rangeAddress, values, null);
    }

    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static String deleteSheet(String workbookId, String sheetId) {

        return retryTemplate.execute(context -> {
            RestTemplate restTemplate = new RestTemplate();
            restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory());
            // 方法1：使用uriVariables包含所有参数
            Map<String, String> uriVariables = new HashMap<>();
            uriVariables.put("workbookId", workbookId);
            uriVariables.put("sheetId", sheetId);
            uriVariables.put("operatorId", operatorId);  // 添加operatorId
            String url = "https://api.dingtalk.com/v1.0/doc/workbooks/{workbookId}/sheets/{sheetId}?operatorId={operatorId}";
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("x-acs-dingtalk-access-token", getToken());

            HttpEntity<String> requestEntity = new HttpEntity<>(headers);

            ResponseEntity<String> response = restTemplate.exchange(
                    url,
                    HttpMethod.DELETE,
                    requestEntity,
                    String.class,
                    uriVariables  // 只传这一个map参数
            );
            lockApiQue();
            if (response.getStatusCode() == HttpStatus.OK) {
                System.out.println("删除成功: " + response.getBody());
            } else {
                System.out.println("删除失败: " + response.getStatusCode());
            }
            return response.getBody();
        });
    }
    /**
     * 查询区域
     * @param workbookId
     * @param sheetId
     * @param rangeAddress A1:B2
     */
    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static String findRanges(String workbookId, String sheetId, String rangeAddress) {
        return retryTemplate.execute(context -> {

            String url = java.lang.String.format("https://api.dingtalk.com/v1.0/doc/workbooks/%s/sheets/%s/ranges/%s?select=%s&operatorId=%s",
                    workbookId, sheetId, rangeAddress, null, operatorId);
            // 设置请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("x-acs-dingtalk-access-token", getToken());
            HttpEntity<String> requestEntity = new HttpEntity<>(headers);
            RestTemplate restTemplate = new RestTemplate();

            ResponseEntity<String> response = restTemplate.exchange(
                    url,
                    HttpMethod.GET,
                    requestEntity,
                    String.class
            );
            JSONObject responseBody = JSONObject.parseObject(response.getBody());
            JSONArray values = responseBody.getJSONArray("values");
            lockApiQue();
//            try {
//                // 休眠3秒（可读性更好）
//                TimeUnit.SECONDS.sleep(sleepTime);
//            } catch (InterruptedException ignored) {}

            System.out.println("Response Status: " + response.getStatusCode());
            System.out.println("Response Body: " + response.getBody());
            System.out.println("values: " + values);
            return response.getBody();
        });
    }

    /**
     * 删除工作表
     * @param workbookId
     * @param sheetId
     */
    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static String delWorkbook(String workbookId, String sheetId) {
        return retryTemplate.execute(context -> {

            String url = java.lang.String.format("https://api.dingtalk.com/v1.0/doc/workbooks/%s/sheets/%s?operatorId=%s",
                    workbookId, sheetId, operatorId);

            // 设置请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("x-acs-dingtalk-access-token", getToken());
            HttpEntity<String> requestEntity = new HttpEntity<>(headers);
            RestTemplate restTemplate = new RestTemplate();
            lockApiQue();
//          try {
//              // 休眠3秒（可读性更好）
//              TimeUnit.SECONDS.sleep(sleepTime);
//          } catch (InterruptedException ignored) {}
            ResponseEntity<String> response = restTemplate.exchange(
                    url, HttpMethod.DELETE, requestEntity, String.class);

            System.out.println("Response Status: " + response.getStatusCode());
            System.out.println("Response Body: " + response.getBody());
            return response.getBody();
        });
    }

    /**
     * 创建表格
     * @param workbookId
     * @param sheetName
     */
    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static JSONObject createSheets(String workbookId, String sheetName) {
        return retryTemplate.execute(context -> {
            String url = java.lang.String.format(
                    "https://api.dingtalk.com/v1.0/doc/workbooks/%s/sheets?operatorId=%s",
                    workbookId, operatorId
            );
            // 构建请求体
            String requestBody = java.lang.String.format("{\"name\":\"%s\"}", sheetName);
            // 设置请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("x-acs-dingtalk-access-token", getToken());
            HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);
            RestTemplate restTemplate = new RestTemplate();
            lockApiQue();
//          try {
//              // 休眠3秒（可读性更好）
//              TimeUnit.SECONDS.sleep(sleepTime);
//          } catch (InterruptedException ignored) {}
            ResponseEntity<String> response = restTemplate.exchange(
                    url,
                    HttpMethod.POST,
                    requestEntity,
                    String.class
            );

            System.out.println("Response Status: " + response.getStatusCode());
            System.out.println("Response Body: " + response.getBody());
            return JSONObject.parseObject(response.getBody());
        });
    }
//  }
//    public static void main(String[] args) {
////        ResponseEntity<String> response = getSheets("pYLaezmVNeKvjaG0FaAA7mmgWrMqPxX6", operatorId, getToken());
////        System.out.println(response.getBody());
////        System.out.println(readSheets("pYLaezmVNeKvjaG0FaAA7mmgWrMqPxX6",""));
//        String workbookId="Obva6QBXJw6lYwAMFlr3arwxVn4qY5Pr";
//           JSONObject wbResult = DDUtil.getSheets(workbookId);
//           System.out.println(wbResult);
//    }


    /**
     *  机器人发送消息
     * @param secret
     * @param content 内容
     * @param webhook
     */
    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static String robotSend(String secret, String content, String webhook) {
        return retryTemplate.execute(context -> {
            try {
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                Long timestamp = System.currentTimeMillis();
                String stringToSign = timestamp + "\n" + secret;
                Mac mac = Mac.getInstance("HmacSHA256");
                mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
                byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
                String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
                //sign字段和timestamp字段必须拼接到请求URL上，否则会出现 310000 的错误信息
                DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?sign=" + sign + "&timestamp=" + timestamp);
                OapiRobotSendRequest req = new OapiRobotSendRequest();
                /**
                 * 发送文本消息
                 */
                //定义文本内容
                OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
                text.setContent(content);
                //定义 @ 对象
                OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
//            at.setAtUserIds(Arrays.asList(USER_ID));
                //设置消息类型
                req.setMsgtype("text");
                req.setText(text);
                req.setAt(at);
                OapiRobotSendResponse rsp = client.execute(req, webhook);
                System.out.println(rsp.getBody());
                return rsp.getBody();
            } catch (ApiException | UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) {
                throw new RuntimeException(e);
            }
        });
    }

    public static String robotSend2(String secret, String content, String webhook) {
        return retryTemplate.execute(context -> {
            try {
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                Long timestamp = System.currentTimeMillis();
                String stringToSign = timestamp + "\n" + secret;
                Mac mac = Mac.getInstance("HmacSHA256");
                mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
                byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
                String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
                //sign字段和timestamp字段必须拼接到请求URL上，否则会出现 310000 的错误信息
                DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?sign=" + sign + "&timestamp=" + timestamp);
                OapiRobotSendRequest req = new OapiRobotSendRequest();
                /**
                 * 发送文本消息
                 */
                //定义文本内容
                OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
                text.setContent(content);
                //定义 @ 对象
                OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
//            at.setAtUserIds(Arrays.asList(USER_ID));
                //设置消息类型
                req.setMsgtype("text");
                req.setText(text);
                req.setAt(at);
                OapiRobotSendResponse rsp = client.execute(req, webhook);
                System.out.println(rsp.getBody());
                return rsp.getBody();
            } catch (ApiException | UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) {
                throw new RuntimeException(e);
            }
        });
    }
    /**
     * 搜索用户userId
     * @param queryWord
     */
    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static JSONObject usersSearch(String queryWord) {
        return retryTemplate.execute(context -> {
            int offset = 0;
            int size = 10;
            int fullMatchField = 0; // Adjust according to API docs
            String url = "https://api.dingtalk.com/v1.0/contact/users/search";
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("x-acs-dingtalk-access-token", getToken());
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("queryWord", queryWord);
            requestBody.put("offset", offset);
            requestBody.put("size", size);
            requestBody.put("fullMatchField", fullMatchField);
            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
            if (response.getStatusCode() == HttpStatus.OK) {
                System.out.println("Response: " + response.getBody());
                return JSONObject.parseObject(response.getBody());
            } else {
                System.out.println("Request failed with status code: " + response.getStatusCode());
                return null;
            }
        });
    }


    /**
     * row 指定行的游标，从0开始。
     * rowCount 插入行的数量。
     * @param row
     * @param rowCount
     */
    /**
     * 读取工作表列表 - 改进后的重试机制
     */

    public static String insertRows(String workbookId, String sheetId, Long row, Long rowCount) {
        return retryTemplate.execute(context -> {
            String url = java.lang.String.format(
                    "https://api.dingtalk.com/v1.0/doc/workbooks/%s/sheets/%s/insertRowsBefore?operatorId=%s",
                    workbookId, sheetId, operatorId);

            // Prepare headers
            HttpHeaders headers = new HttpHeaders();
            headers.set("x-acs-dingtalk-access-token", getToken());
            headers.set("Host", "api.dingtalk.com");
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));

            // Prepare request body
            Map<String, Long> requestBody = new HashMap<>();
            requestBody.put("row", row);
            requestBody.put("rowCount", rowCount);

            // Create the HTTP entity
            HttpEntity<Map<String, Long>> requestEntity = new HttpEntity<>(requestBody, headers);
            RestTemplate restTemplate = new RestTemplate();
            try {
                // Make the request
                ResponseEntity<String> response = restTemplate.exchange(
                        url,
                        HttpMethod.POST,
                        requestEntity,
                        String.class);

                // Check response status
                if (response.getStatusCode().is2xxSuccessful()) {
                    return response.getBody();
                } else {
                    // Handle error responses
                    String errorDetails = response.getStatusCode() + " - " + response.getBody();
                    throw new RuntimeException("Failed to insert rows. Error: " + errorDetails);
                }
            } catch (Exception e) {
                throw new RuntimeException("Error calling DingTalk API to insert rows", e);
            }
        });
    }

    /**
     * 删除工作表中的行
     *
     * @param workbookId 工作簿ID
     * @param sheetId    工作表ID
     * @param startRow   起始行号(从0开始)
     * @param rowCount   要删除的行数
     * @return 是否删除成功
     */
    public static boolean deleteRows(String workbookId, String sheetId, long startRow,
                                     long rowCount) {
        return retryTemplate.execute(context -> {
            try {

                // 1. 构建请求URL
                String url = java.lang.String.format(
                        "https://api.dingtalk.com/v1.0/doc/workbooks/%s/sheets/%s/deleteRows?operatorId=%s",
                        encode(workbookId),
                        encode(sheetId),
                        encode(operatorId)
                );

                // 2. 构建请求体
                Map<String, Long> requestBody = new HashMap<>();
                requestBody.put("row", startRow);
                requestBody.put("rowCount", rowCount);

                // 3. 设置请求头
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                headers.set("x-acs-dingtalk-access-token", getToken());

                // 4. 创建请求实体
                HttpEntity<Map<String, Long>> requestEntity = new HttpEntity<>(requestBody, headers);
//            try {
//                // 休眠3秒（可读性更好）
//                TimeUnit.SECONDS.sleep(sleepTime);
//            } catch (InterruptedException ignored) {}
                lockApiQue();
                // 5. 发送POST请求
                ResponseEntity<String> response = getRestTemplate().exchange(
                        url,
                        HttpMethod.POST,
                        requestEntity,
                        String.class
                );

                // 6. 检查响应状态
                if (response.getStatusCode().is2xxSuccessful()) {
                    return true;
                } else {
                    throw new RuntimeException(response.getBody());

                }

            } catch (Exception e) {

                throw new RuntimeException("调用钉钉API失败");
            }
        });
    }

    private static String encode(String param) {
        try {
            return URLEncoder.encode(param, java.lang.String.valueOf(StandardCharsets.UTF_8));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    private static void lockApiQue() {
        String ddApiQueKeyVal = getRedisService().getString(ddApiQueKey);
        if (StringUtils.isNotBlank(ddApiQueKeyVal)) {
            Integer consecutiveReqNumVal = Integer.parseInt(ddApiQueKeyVal);
            if (consecutiveReqNumVal > consecutiveReqNum) {
                try {
                    TimeUnit.SECONDS.sleep(sleepTime);
                } catch (InterruptedException ignored) {
                }
            }
            ++consecutiveReqNumVal;
            getRedisService().setWithExpire(ddApiQueKey, consecutiveReqNumVal.toString(), sleepTime, TimeUnit.SECONDS);
        } else {
            getRedisService().setWithExpire(ddApiQueKey, "1", sleepTime, TimeUnit.SECONDS);
        }

    }

    /*    public static void main(String[] args) {
     *//*        String workbookId="Obva6QBXJw6lYwAMFlr3arwxVn4qY5Pr";
     *//**//*    JSONObject wbResult = DDUtil.getSheets(workbookId);
            JSONArray value = wbResult.getJSONArray("value");
            List<String> ids = value.stream().filter(val -> {
            JSONObject nval = (JSONObject) val;String name = nval.getString("name");
            return name.equals("Sheet1");}).map(val->{JSONObject nval = (JSONObject) val;
            return nval.getString("id");
            }).collect(Collectors.toList());
            String sheetId="";
            if(!ids.isEmpty()){
                sheetId=ids.get(0);
            }*//**//*
        String sheetId="s1";
        DDUtil.deleteRows(workbookId,sheetId,11,1);*//*
        String text = "这是第一行。\r\n这是第二行。\n这是第三行。\r这是第四行。";

        // 使用正则表达式匹配所有的换行符
        String cleanedText = text.replaceAll("[\\r\\n]+", "");

        System.out.println(cleanedText);
    }*/
    private static final DateTimeFormatter FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static List<String> getHourlyTimesBetween(LocalDateTime start, LocalDateTime end) {
        if (start == null || end == null || start.isAfter(end)) {
            return Collections.emptyList();
        }

        // 计算时间间隔（30分钟）
        Duration interval = Duration.ofMinutes(30);

        // 计算需要多少个30分钟间隔
        long numberOfIntervals = Duration.between(start, end).toMinutes() / 30;

        // 使用Stream生成时间序列
        return Stream.iterate(start, time -> time.plus(interval))
                .limit(numberOfIntervals + 1)  // +1 包含起始时间
                .map(FORMATTER::format)
                .collect(Collectors.toList());
    }
//    public static void main(String[] args) {
//        LocalDateTime today = LocalDateTime.now();
//        LocalDateTime endTime = today;
////        LocalDateTime startTime = today.minusMinutes(30);
//        LocalDateTime startTime = today.minusHours(2);
//        List<String> hourlyTimesBetween = getHourlyTimesBetween(startTime, endTime);
////        for (String hour : hourlyTimesBetween){
//        for (int i=1; i<hourlyTimesBetween.size();i++){
//            System.out.println(hourlyTimesBetween.get(i-1)+" "+hourlyTimesBetween.get(i));
//        }
//    }

//    public static void main(String[] args) {
//        LocalDateTime dateTime = LocalDateTime.now();
//

    /// /        // 截断到分钟（去掉秒和毫秒）
    /// /        LocalDateTime truncated = dateTime.truncatedTo(ChronoUnit.MINUTES);
//
//        System.out.println(dateTime.getHour());
//    }

//    public static void main(String[] args) {
//        System.out.println(readSheets("G1DKw2zgV2yXd0g4INQqZYboWB5r9YAn","订单拦截统计表","A1:F5001"));
//    }
//    String S ="SEC487fd22f2fa15f69cdbeff10037ff928027918724098bfd86b99d8016be32d7b";
//    String S ="9b0ac3ca436c87bad260c8510b93e6c0a6ba3ffe3ad1b9ebab159ba710dc2df9";
    public static void sendMessageWebhook(String secret, String token, String content) {
        sendMessageWebhook(secret, token, content, null, null);
    }

    public static void sendMessageWebhookMobiles(String secret, String token, String content, List<String> atMobiles) {
        sendMessageWebhook(secret, token, content, null, atMobiles);
    }

    public static void sendMessageWebhookUserIds(String secret, String token, String content, List<String> atUserIds) {
        sendMessageWebhook(secret, token, content, atUserIds, null);
    }

    public static void sendMessageWebhook(String secret, String token, String content, List<String> atUserIds, List<String> atMobiles) {
        Long timestamp = System.currentTimeMillis();
        String stringToSign = timestamp + "\n" + secret;
        Mac mac = null;
        String sign = null;
        try {
            mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
            sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
            String url = "https://oapi.dingtalk.com/robot/send?access_token=" + token + "&timestamp=" + timestamp + "&sign=" + sign;
            DingTalkClient client = new DefaultDingTalkClient(url);
            OapiRobotSendRequest request = new OapiRobotSendRequest();
            request.setMsgtype("text");
            OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
            text.setContent(content);
            request.setText(text);
            OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
            at.setAtUserIds(Collections.singletonList(operatorId));
            at.setIsAtAll(false);
            at.setAtUserIds(atUserIds);
            at.setAtMobiles(atMobiles);
            request.setAt(at);
            OapiRobotSendResponse response = client.execute(request);
        } catch (ApiException | InvalidKeyException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 获取表格上次追加行数
     *
     * @param rangeAddressKey
     * @param workbookId
     * @param sheetId
     * @return
     */
    public static Integer optimizeTable(String rangeAddressKey, String workbookId, String sheetId, String rangeAddressTemplate, List<String> headerValues, Integer readBatchSize, Integer dataSaveDays, TimeUnit timeUnit) {
        String currentSubscriptStr = redisService.getString(rangeAddressKey);
        Integer currentSubscript = 1;
        if (StringUtils.isBlank(currentSubscriptStr)) {
            List<String> findSheets = DDUtil.getSheets(workbookId).getJSONArray("value").stream().map(val -> {
                JSONObject vm = (JSONObject) val;
                return vm.getString("name");
            }).filter(name -> name.equals(sheetId)).collect(Collectors.toList());
            if (!findSheets.isEmpty()) {
                if (JSONObject.parseObject(DDUtil.readSheets(workbookId, sheetId, java.lang.String.format(rangeAddressTemplate, currentSubscript, currentSubscript))).getJSONArray("values").stream().map(v -> (List<Object>) v).filter(v -> StringUtils.isNotBlank(v.get(0).toString())).count() == 0) {
                    String rangeAddress = java.lang.String.format(rangeAddressTemplate, currentSubscript, currentSubscript);
                    DDUtil.updateRange(workbookId, sheetId, rangeAddress, Collections.singletonList(headerValues));
                }


                Integer startIndex = currentSubscript;
                Integer endIndex = startIndex + readBatchSize;
                currentSubscript = 0;
                for (int i = 0; i < 100; i++) {
                    String rangeAddress = java.lang.String.format(rangeAddressTemplate, startIndex, endIndex);
                    JSONObject rsData = JSONObject.parseObject(DDUtil.readSheets(workbookId, sheetId, rangeAddress));
                    long rssData = rsData.getJSONArray("values").stream().map(v -> (List<Object>) v).filter(v -> StringUtils.isNotBlank(v.get(0).toString())).count();
                    currentSubscript = Math.toIntExact(currentSubscript + rssData);
                    if (rssData == 0) {
                        break;
                    }
                    startIndex = endIndex + 1;
                    endIndex = endIndex + readBatchSize;
                }
                getRedisService().setWithExpire(rangeAddressKey, currentSubscript.toString(), dataSaveDays, timeUnit);
            } else {
                DDUtil.createSheets(workbookId, sheetId);
                String rangeAddress = java.lang.String.format(rangeAddressTemplate, currentSubscript, currentSubscript);
                DDUtil.updateRange(workbookId, sheetId, rangeAddress, Collections.singletonList(headerValues));
                getRedisService().setWithExpire(rangeAddressKey, currentSubscript.toString(), dataSaveDays, timeUnit);
            }
        } else {
            currentSubscript = Integer.parseInt(currentSubscriptStr);
        }
        return currentSubscript;
    }

    /**
     * 批量更新表格
     *
     * @param workbookId
     * @param sheetId
     * @param values
     * @param currentSubscript
     * @param rangeAddressKey
     */
    public static void importValuesData(String workbookId, String sheetId, List<List<String>> values, Integer currentSubscript, String rangeAddressKey, String rangeAddressTemplate, Integer updateBatchSize, Integer dataSaveDays, TimeUnit timeUnit) {
        int totalSize = values.size();
        Integer startIndex = currentSubscript;
        Integer endIndex = startIndex;
        for (int start = 0; start < totalSize; start += updateBatchSize) {
            int end = Math.min(start + updateBatchSize, totalSize);
            List<List<String>> batchValues = values.subList(start, end);
            startIndex = endIndex + 1;
            endIndex = startIndex + batchValues.size() - 1;
            String rangeAddress = java.lang.String.format(rangeAddressTemplate, startIndex, endIndex);
            DDUtil.updateRange(workbookId, sheetId, rangeAddress, batchValues);
        }
        getRedisService().setWithExpire(rangeAddressKey, endIndex.toString(), dataSaveDays, timeUnit);
    }

    public static void updateDocumentSending(String workbookId, String secret, String token, List<String> atMobiles, List<String> atUserIds, List<List<String>> values, String sheetId, List<String> headerValues,
                                             String msgTitle, String msgTemplate, String endingContent, String rangeAddressTemplate, Integer updateBatchSize, Integer dataSaveDays, TimeUnit timeUnit, String keyTemplate) {
        String rangeAddressKey = java.lang.String.format(keyTemplate, workbookId, sheetId);
        Integer currentSubscript = DDUtil.optimizeTable(rangeAddressKey, workbookId, sheetId, rangeAddressTemplate, headerValues, updateBatchSize, dataSaveDays, timeUnit);
        DDUtil.importValuesData(workbookId, sheetId, values, currentSubscript, rangeAddressKey, rangeAddressTemplate, updateBatchSize, dataSaveDays, timeUnit);
        List<List<List<String>>> partitionValues = Lists.partition(values, 9);
        if (StringUtils.isBlank(msgTemplate) || StringUtils.isBlank(endingContent)) {
            String content = msgTitle.concat("\n").concat(java.lang.String.format(ddLinkUrl, workbookId));
            DDUtil.sendMessageWebhook(secret, token, content, atUserIds, atMobiles);
        } else {
            for (int i = 0; i < partitionValues.size(); i++) {
                List<List<String>> pv = partitionValues.get(i);
                String content = msgTitle;
                content = content.concat("\n\n").concat(
                        pv.stream().map(cpv -> java.lang.String.format(msgTemplate, cpv.toArray()).concat("\n")).collect(Collectors.joining("\n")));
                content = content.concat("\n").concat(endingContent);
                content = content.concat("\n").concat(java.lang.String.format(ddLinkUrl, workbookId));
                DDUtil.sendMessageWebhook(secret, token, content, atUserIds, atMobiles);
            }
        }
    }
//    public static void main(String[] args) {
//        sendMessageWebhookMobiles("SEC29cb16689d420846a5b9bc319c2b33c5e0046097e9ec45ed9c255552300cee2f","8ed9de0a29deae71b5adf2cc1b2542a2ebdd1d863b0ee96c42ca948a76a5cd55","测试", Arrays.asList("18890076307","15574322919"));
//    }


    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 获取知识库列表
     *
     * @param nextToken          分页游标(首次可不传)
     * @param maxResults         分页大小(默认30)
     * @param orderBy            排序方式
     * @param withPermissionRole 是否查询权限
     * @return 知识库列表响应
     * @throws Exception
     */
    public static WorkspaceListResponse getWorkspaceList(String nextToken, Integer maxResults,
                                                         String orderBy, Boolean withPermissionRole) {
        // 构建查询参数
        StringBuilder queryParams = new StringBuilder();
        try {
            queryParams.append("operatorId=").append(URLEncoder.encode(operatorId, java.lang.String.valueOf(StandardCharsets.UTF_8)));


            if (nextToken != null && !nextToken.isEmpty()) {
                queryParams.append("&nextToken=").append(URLEncoder.encode(nextToken, java.lang.String.valueOf(StandardCharsets.UTF_8)));
            }

            if (maxResults != null) {
                queryParams.append("&maxResults=").append(maxResults);
            }

            if (orderBy != null && !orderBy.isEmpty()) {
                queryParams.append("&orderBy=").append(URLEncoder.encode(orderBy, java.lang.String.valueOf(StandardCharsets.UTF_8)));
            }

            if (withPermissionRole != null) {
                queryParams.append("&withPermissionRole=").append(withPermissionRole);
            }

            String url = "https://api.dingtalk.com/v2.0/wiki/workspaces?" + queryParams;

            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(url);

            // 设置请求头
            httpGet.setHeader("x-acs-dingtalk-access-token", getToken());
            httpGet.setHeader("Content-Type", "application/json");

            // 执行请求
            CloseableHttpResponse response = null;
            response = httpClient.execute(httpGet);

            String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

            // 解析响应
            if (response.getStatusLine().getStatusCode() == 200) {
                return objectMapper.readValue(responseString, WorkspaceListResponse.class);
            } else {
                throw new RuntimeException("API调用失败: " + responseString);
            }
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }

    }

    /**
     * 在知识库中创建文档
     *
     * @param workspaceId 知识库ID
     * @param request     创建请求参数
     * @return 创建响应
     * @throws Exception
     */
    public static CreateDocResponse createDocument(String workspaceId, CreateDocRequest request) {
        request.setOperatorId(operatorId);
        String url = "https://api.dingtalk.com/v1.0/doc/workspaces/" + workspaceId + "/docs";

        try {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(url);

            // 设置请求头
            httpPost.setHeader("x-acs-dingtalk-access-token", getToken());
            httpPost.setHeader("Content-Type", "application/json");

            // 设置请求体
            String requestBody = objectMapper.writeValueAsString(request);
            httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));

            // 执行请求
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

            // 解析响应
            if (response.getStatusLine().getStatusCode() == 200) {
                return objectMapper.readValue(responseString, CreateDocResponse.class);
            } else {
                throw new RuntimeException("API调用失败: " + responseString);
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        } catch (ClientProtocolException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 创建AI表数据表
     *
     * @param baseId  AI表格ID
     * @param request 创建请求参数
     * @return 创建响应
     * @throws RuntimeException API调用异常
     */
    public static CreateAiSheetResponse createAiSheet(String baseId, CreateAiSheetRequest request) {
        String url = java.lang.String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets?operatorId=%s", baseId, operatorId);
        try {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(url);

            // 设置请求头
            httpPost.setHeader("x-acs-dingtalk-access-token", getToken());
            httpPost.setHeader("Content-Type", "application/json");
            httpPost.setHeader("Accept", "application/json");

            // 设置请求体
            String requestBody = objectMapper.writeValueAsString(request);
            httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));

            // 执行请求并处理响应
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String responseBody = EntityUtils.toString(response.getEntity());

            if (response.getStatusLine().getStatusCode() == 200) {
                return objectMapper.readValue(responseBody, CreateAiSheetResponse.class);
            } else {
                // 解析错误响应
                CreateAiSheetResponse.ErrorResponse errorResponse = objectMapper.readValue(responseBody, CreateAiSheetResponse.ErrorResponse.class);
                throw new RuntimeException(java.lang.String.format("API调用失败: code=%s, message=%s, requestId=%s", errorResponse.getCode(), errorResponse.getMessage(), errorResponse.getRequestId()));
            }
        } catch (IOException e) {
            throw new RuntimeException("调用钉钉API异常", e);
        }
    }

    /**
     * 删除AI表格
     *
     * @param baseId  AI表格ID
     * @param sheetId 工作表id
     * @return 删除响应
     * @throws Exception
     */
    public static void deleteAiSheet(String baseId, String sheetId) {
        try {
            String urlStr = java.lang.String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s?operatorId=%s",
                    baseId, sheetId, operatorId);
            URL url = new URL(urlStr);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("DELETE");
            connection.setRequestProperty("x-acs-dingtalk-access-token", getToken());
            connection.setRequestProperty("Content-Type", "application/json");
            int responseCode = connection.getResponseCode();
            System.out.println("Response Code: " + responseCode);

            // 读取响应内容
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String inputLine;
            StringBuilder response = new StringBuilder();

            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();
            // 打印响应结果
            System.out.println(response.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取AI表名
     *
     * @param baseId  AI表格ID
     * @param sheetId 数据表ID
     * @return 数据表信息
     * @throws RuntimeException API调用异常
     */
    public static GetSheetResponse getAiSheet(String baseId, String sheetId) {
        String url = java.lang.String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s?operatorId=%s",
                baseId, sheetId, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(url);

            // 设置请求头
            httpGet.setHeader("x-acs-dingtalk-access-token", getToken());
            httpGet.setHeader("Content-Type", "application/json");
            httpGet.setHeader("Accept", "application/json");

            // 执行请求并处理响应
            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                int statusCode = response.getStatusLine().getStatusCode();

                if (statusCode == 200) {
                    return objectMapper.readValue(responseBody, GetSheetResponse.class);
                } else {
                    // 解析错误响应
                    GetSheetResponse.DingTalkErrorResponse errorResponse = objectMapper.readValue(responseBody, GetSheetResponse.DingTalkErrorResponse.class);
                    throw new RuntimeException(errorResponse.toString());
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("调用钉钉API异常", e);
        }
    }

    /**
     * 查询AI表所有工作表
     *
     * @param baseId AI表格ID
     * @return 创建响应
     * @throws RuntimeException API调用异常
     */
    public static AiSheetListResponse getAiSheets(String baseId) {
        String url = String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets?operatorId=%s",
                baseId, operatorId);

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(url);

            // Set headers
            httpGet.setHeader("x-acs-dingtalk-access-token", getToken());
            httpGet.setHeader("Content-Type", "application/json");

            // Execute request
            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                if (response.getStatusLine().getStatusCode() == 200) {
                    return objectMapper.readValue(responseBody, AiSheetListResponse.class);
                } else {
                    // Parse error response
                    AiSheetListResponse.ErrorResponse error = objectMapper.readValue(responseBody, AiSheetListResponse.ErrorResponse.class);
                    throw new RuntimeException(java.lang.String.format("API call failed: %s (code: %s)",
                            error.getMessage(), error.getCode()));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to get sheets", e);
        }
    }

    /**
     * 更新AI表名
     *
     * @param baseId AI表格ID
     * @return 创建响应
     * @throws RuntimeException API调用异常
     */
    public static void updateAiSheetsName(String baseId, String sheetId, String newSheetName) {
        try {
            String urlStr = java.lang.String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s?operatorId=%s", baseId, sheetId, operatorId);
            URL url = new URL(urlStr);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("PUT");
            connection.setDoOutput(true);
            connection.setRequestProperty("x-acs-dingtalk-access-token", getToken());
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("Accept", "application/json");
            String requestBody = java.lang.String.format("{\"name\":\"%s\"}", newSheetName);
            try (OutputStream os = connection.getOutputStream()) {
                byte[] input = requestBody.getBytes(StandardCharsets.UTF_8);
                os.write(input, 0, input.length);
            }
            int responseCode = connection.getResponseCode();
            System.out.println("Response Code: " + responseCode);
            BufferedReader in;
            if (responseCode >= 200 && responseCode < 300) {
                in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            } else {
                in = new BufferedReader(new InputStreamReader(connection.getErrorStream()));
            }

            String inputLine;
            StringBuilder response = new StringBuilder();

            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();

            // 打印响应结果
            System.out.println("Response: " + response.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新AI表记录
     *
     * @param baseId  AI表格ID
     * @param sheetId AI工作表ID
     * @param records 内容
     * @return 创建响应
     * @throws RuntimeException API调用异常
     */
    public static AddAiSheetRecordsResponse addAiSheetRecords(String baseId, String sheetId, AddAiSheetRecordsRequest.Record[] records) {
        if (StringUtils.isBlank(baseId)) {
            baseId = docId;
        }
        String url = java.lang.String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s/records?operatorId=%s", baseId, sheetId, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("x-acs-dingtalk-access-token", getToken());
            httpPost.setHeader("Content-Type", "application/json");
            AddAiSheetRecordsRequest request = new AddAiSheetRecordsRequest();
            request.setRecords(records);
            String requestBody = objectMapper.writeValueAsString(request);
            httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));
            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                log.info("新增AI表记录结果返回:{}", responseBody);
                if (response.getStatusLine().getStatusCode() == 200) {
                    return objectMapper.readValue(responseBody, AddAiSheetRecordsResponse.class);
                } else {
                    AddAiSheetRecordsResponse.ErrorResponse error = objectMapper.readValue(responseBody, AddAiSheetRecordsResponse.ErrorResponse.class);
                    throw new RuntimeException(java.lang.String.format("API call failed: %s (code: %s)", error.getMessage(), error.getCode()));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to add records", e);
        }
    }


    /**
     * 更新AI表记录
     *
     * @param baseId  AI表格ID
     * @param sheetId AI工作表ID
     * @param records 内容
     * @return 创建响应
     * @throws RuntimeException API调用异常
     */
    public static AddAiSheetRecordsResponse addAiSheetRecordsBatch(String baseId, String sheetId, AddAiSheetRecordsRequest.Record[] records) {
        return addAiSheetRecords(baseId, sheetId, records);
    }

    /**
     * 获取AI表内容
     *
     * @param baseId   AI表格ID
     * @param sheetId  数据表ID或数据表名称
     * @param recordId 记录ID
     * @return 创建响应
     * @throws RuntimeException API调用异常
     */
    public static GetRecordsResponse getAiSheetRecord(String baseId, String sheetId, String recordId) {
        String url = String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s/records/%s?operatorId=%s",
                baseId, sheetId, recordId, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("x-acs-dingtalk-access-token", getToken());
            httpGet.setHeader("Content-Type", "application/json");
            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                if (response.getStatusLine().getStatusCode() == 200) {
                    return objectMapper.readValue(responseBody, GetRecordsResponse.class);
                } else {
                    GetRecordsResponse.ErrorResponse error = objectMapper.readValue(responseBody, GetRecordsResponse.ErrorResponse.class);
                    throw new RuntimeException(String.format("API call failed: %s (code: %s)",
                            error.getMessage(), error.getCode()));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to get record", e);
        }
    }

    /**
     * AI工作表列出多行记录
     *
     * @param baseId     AI表格ID
     * @param sheetId    数据表ID或数据表名称
     * @param maxResults 每页获取的数据量，默认值为100，最小值为1，最大值为100
     * @param nextToken  上一次查询返回的游标，首次查询时不需要传
     * @return
     */
    public static ListRecordsResponse listRecords(String baseId, String sheetId, Integer maxResults, String nextToken) {
        String url = String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s/records/list?operatorId=%s", baseId, sheetId, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("x-acs-dingtalk-access-token", getToken());
            httpPost.setHeader("Content-Type", "application/json");
            ListRecordsRequest request = new ListRecordsRequest();
            request.setMaxResults(maxResults);
            request.setNextToken(nextToken);
            String requestBody = objectMapper.writeValueAsString(request);
            httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));
            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                if (response.getStatusLine().getStatusCode() == 200) {
                    return objectMapper.readValue(responseBody, ListRecordsResponse.class);
                } else {
                    ListRecordsResponse.ErrorResponse error = objectMapper.readValue(responseBody, ListRecordsResponse.ErrorResponse.class);
                    throw new RuntimeException(String.format("API call failed: %s (code: %s)", error.getMessage(), error.getCode()));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to list records", e);
        }
    }

    /**
     * 更新AiSheet多行记录
     *
     * @param baseId  AI表格ID
     * @param sheetId 数据表ID或数据表名称
     * @param records 需更新的记录
     * @return
     * @throws Exception
     */
    public static UpdateAiSheetRecordsResponse updateAiSheetRecords(String baseId, String sheetId, List<UpdateAiSheetRecordsRequest.Record> records) {
        String url = String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s/records?operatorId=%s", baseId, sheetId, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPut httpPut = new HttpPut(url);
            httpPut.setHeader("x-acs-dingtalk-access-token", getToken());
            httpPut.setHeader("Content-Type", "application/json");
            UpdateAiSheetRecordsRequest request = new UpdateAiSheetRecordsRequest();
            request.setRecords(records);
            String requestBody = objectMapper.writeValueAsString(request);
            httpPut.setEntity(new StringEntity(requestBody, "UTF-8"));
            try (CloseableHttpResponse response = httpClient.execute(httpPut)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                if (response.getStatusLine().getStatusCode() == 200) {
                    return objectMapper.readValue(responseBody, UpdateAiSheetRecordsResponse.class);
                } else {
                    UpdateAiSheetRecordsResponse.ErrorResponse error = objectMapper.readValue(responseBody, UpdateAiSheetRecordsResponse.ErrorResponse.class);
                    throw new RuntimeException(String.format("API call failed: %s (code: %s)", error.getMessage(), error.getCode()));
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除AiSheet多行记录
     *
     * @param baseId    AI表格ID
     * @param sheetId   数据表ID或数据表名称
     * @param recordIds 记录ID
     * @return
     * @throws Exception
     */
    public static DeleteAiSheetRecordsResponse deleteRecords(String baseId, String sheetId, List<String> recordIds) {
        String url = String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s/records/delete?operatorId=%s",
                baseId, sheetId, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("x-acs-dingtalk-access-token", getToken());
            httpPost.setHeader("Content-Type", "application/json");
            DeleteAiSheetRecordsRequest request = new DeleteAiSheetRecordsRequest();
            request.setRecordIds(recordIds);
            String requestBody = objectMapper.writeValueAsString(request);
            httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));
            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                if (response.getStatusLine().getStatusCode() == 200) {
                    return objectMapper.readValue(responseBody, DeleteAiSheetRecordsResponse.class);
                } else {
                    DeleteAiSheetRecordsResponse.ErrorResponse error = objectMapper.readValue(responseBody, DeleteAiSheetRecordsResponse.ErrorResponse.class);
                    throw new RuntimeException(String.format(
                            "API call failed: %s (code: %s)",
                            error.getMessage(),
                            error.getCode()));
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建ai工作表字段
     *
     * @param baseId
     * @param sheetId
     * @param fieldName
     * @param fieldType
     * @param properties
     * @return
     * @throws Exception
     */
    public static CreateAiSheetFieldResponse createAiSheetField(String baseId, String sheetId, String fieldName, String fieldType, Map<String, Object> properties) throws Exception {
        String url = String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s/fields?operatorId=%s", baseId, sheetId, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(url);

            // Set headers
            httpPost.setHeader("x-acs-dingtalk-access-token", getToken());
            httpPost.setHeader("Content-Type", "application/json");

            // Build request body
            CreateAiSheetFieldRequest request = new CreateAiSheetFieldRequest();
            request.setName(fieldName);
            request.setType(fieldType);
            request.setProperty(properties);

            String requestBody = objectMapper.writeValueAsString(request);
            httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));

            // Execute request
            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                String responseBody = EntityUtils.toString(response.getEntity());

                if (response.getStatusLine().getStatusCode() == 200) {
                    return objectMapper.readValue(responseBody, CreateAiSheetFieldResponse.class);
                } else {
                    CreateAiSheetFieldResponse.ErrorResponse error = objectMapper.readValue(responseBody, CreateAiSheetFieldResponse.ErrorResponse.class);
                    throw new RuntimeException(String.format("API call failed: %s (code: %s)", error.getMessage(), error.getCode()));
                }
            }
        }
    }

    /**
     * 更新ai工作表字段
     *
     * @param baseId
     * @param sheetId
     * @param fieldIdOrName
     * @param newName
     * @param properties
     * @return
     */
    public static UpdateAiSheetFieldResponse updateAiSheetField(String baseId, String sheetId, String fieldIdOrName, String newName, Map<String, Object> properties) {
        String url = String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s/fields/%s?operatorId=%s",
                baseId, sheetId, fieldIdOrName, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPut httpPut = new HttpPut(url);
            httpPut.setHeader("x-acs-dingtalk-access-token", getToken());
            httpPut.setHeader("Content-Type", "application/json");
            UpdateAiSheetFieldRequest request = new UpdateAiSheetFieldRequest();
            request.setName(newName);
            request.setProperty(properties);
            String requestBody = objectMapper.writeValueAsString(request);
            httpPut.setEntity(new StringEntity(requestBody, "UTF-8"));
            try (CloseableHttpResponse response = httpClient.execute(httpPut)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                if (response.getStatusLine().getStatusCode() == 200) {
                    return objectMapper.readValue(responseBody, UpdateAiSheetFieldResponse.class);
                } else {
                    UpdateAiSheetFieldResponse.ErrorResponse error = objectMapper.readValue(responseBody, UpdateAiSheetFieldResponse.ErrorResponse.class);
                    throw new RuntimeException(String.format("API call failed: %s (code: %s)", error.getMessage(), error.getCode()));
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取所有的工作表字段
     *
     * @param baseId
     * @param sheetId
     * @return
     */
    public static List<AiSheetFieldListResponse.Field> getAllAiSheetFields(String baseId, String sheetId) {
        String url = String.format(
                "https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s/fields?operatorId=%s",
                baseId, sheetId, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("x-acs-dingtalk-access-token", getToken());
            httpGet.setHeader("Content-Type", "application/json");
            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                if (response.getStatusLine().getStatusCode() == 200) {
                    AiSheetFieldListResponse fieldList = objectMapper.readValue(responseBody, AiSheetFieldListResponse.class);
                    return fieldList.getValue();
                } else {
                    AiSheetFieldListResponse.ErrorResponse error = objectMapper.readValue(responseBody, AiSheetFieldListResponse.ErrorResponse.class);
                    throw new RuntimeException(String.format("API call failed: %s (code: %s)", error.getMessage(), error.getCode()));
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除ai表格
     *
     * @param baseId
     * @param sheetId
     * @param fieldIdOrName
     * @return
     */
    public static DeleteAiSheetFieldResponse deleteAiSheetField(String baseId, String sheetId, String fieldIdOrName) {
        String url = String.format("https://api.dingtalk.com/v1.0/notable/bases/%s/sheets/%s/fields/%s?operatorId=%s",
                baseId, sheetId, fieldIdOrName, operatorId);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpDelete httpDelete = new HttpDelete(url);
            httpDelete.setHeader("x-acs-dingtalk-access-token", getToken());
            httpDelete.setHeader("Content-Type", "application/json");
            try (CloseableHttpResponse response = httpClient.execute(httpDelete)) {
                String responseBody = EntityUtils.toString(response.getEntity());

                if (response.getStatusLine().getStatusCode() == 200) {
                    return objectMapper.readValue(responseBody, DeleteAiSheetFieldResponse.class);
                } else {
                    DeleteAiSheetFieldResponse.ErrorResponse error = objectMapper.readValue(responseBody, DeleteAiSheetFieldResponse.ErrorResponse.class);
                    throw new RuntimeException(String.format("API call failed: %s (code: %s)", error.getMessage(), error.getCode()));
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 初始化
     *
     * @param sheetName
     * @param headerValues
     * @return
     */
    public static CreateAiSheetRequest initCreateAiSheetRequest(String sheetName, List<CreateAiSheetFieldRequest> headerValues) {
        CreateAiSheetRequest request = new CreateAiSheetRequest();
        request.setName(sheetName);
        List<CreateAiSheetRequest.FieldConfig> fields = headerValues.stream().map(hv -> new CreateAiSheetRequest.FieldConfig(hv.getName(), hv.getType(), null)).collect(Collectors.toList());
        request.setFields(fields);
        return request;
    }

    public static List<AddAiSheetRecordsRequest.Record> importAiExcel(String workbookId, List<List<String>> valuesData, String sheetName, List<String> headerValues, Integer updateBatchSize) {
        // 2. 构建记录列表
        List<AddAiSheetRecordsRequest.Record> records = new ArrayList<>();
        for (List<String> value : valuesData) {
            AddAiSheetRecordsRequest.Record record = new AddAiSheetRecordsRequest.Record();
            Map<String, Object> fields = new HashMap<>();
            for (int i = 0; i < headerValues.size(); i++) {
                fields.put(headerValues.get(i), value.get(i));
            }
            record.setFields(fields);
            records.add(record);
        }
        try {
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        List<GetSheetResponse> findSheet = Arrays.stream(getAiSheets(workbookId).getValue()).filter(v -> ObjectUtil.equal(v.getName(), sheetName)).collect(Collectors.toList());
        String sheetId = "";
        if (findSheet.isEmpty()) {
            List<CreateAiSheetFieldRequest> headerFields = headerValues.stream().map(hv -> new CreateAiSheetFieldRequest(hv, DDFieldType.TEXT.getCode())).collect(Collectors.toList());
            CreateAiSheetRequest createAiSheetRequest = initCreateAiSheetRequest(sheetName, headerFields);
            CreateAiSheetResponse aiSheet = createAiSheet(workbookId, createAiSheetRequest);
            sheetId = aiSheet.getId();
        } else {
            sheetId = findSheet.get(0).getId();
        }
        List<List<AddAiSheetRecordsRequest.Record>> values = Lists.partition(records, updateBatchSize);
        for (List<AddAiSheetRecordsRequest.Record> value : values) {
            AddAiSheetRecordsRequest.Record[] array = value.toArray(new AddAiSheetRecordsRequest.Record[0]);

            AddAiSheetRecordsResponse addAiSheetRecordsResponse = addAiSheetRecords(workbookId, sheetId, array);
        }
        return records;
    }

    /**
     * 更新ai表格发送消息
     *
     * @param workbookId
     * @param secret
     * @param token
     * @param atMobiles
     * @param atUserIds
     * @param sheetName
     * @param headerValues
     * @param msgTitle
     * @param msgTemplate
     * @param endingContent
     * @param updateBatchSize
     */
    public static void updateAiExcelSendMsg(String workbookId, String secret, String token, List<String> atMobiles, List<String> atUserIds, List<List<String>> valuesData, String sheetName,
                                            List<String> headerValues, String msgTitle, String msgTemplate, String endingContent, Integer updateBatchSize) {
       /* // 2. 构建记录列表
        List<AddSmartSheetRecordsRequest.Record> records = new ArrayList<>();
        for(List<String> value:valuesData){
            Map<String, List<AddSmartSheetRecordsRequest.Record.CellValue>> recordCellValue = new HashMap<>();
            for (int i=0;i<headerValues.size();i++){
                recordCellValue.put(headerValues.get(i),Collections.singletonList(AddSmartSheetRecordsRequest.Record.CellValue.createText(value.get(i))));
            }
            records.add(new AddSmartSheetRecordsRequest.Record(recordCellValue));
        }


        List<GetSheetResponse> findSheet = Arrays.stream(getAiSheets(workbookId).getValue()).filter(v -> ObjectUtil.equal(v.getName(), sheetName)).collect(Collectors.toList());
        String sheetId="";
        if(findSheet.isEmpty()){
            List<CreateAiSheetFieldRequest> headerFields = headerValues.stream().map(hv -> new CreateAiSheetFieldRequest(hv, DDFieldType.TEXT.getCode())).collect(Collectors.toList());
            CreateAiSheetRequest createAiSheetRequest = initCreateAiSheetRequest(sheetName, headerFields);
            CreateAiSheetResponse aiSheet = createAiSheet(workbookId, createAiSheetRequest);
            sheetId=aiSheet.getId();
        }else{
            sheetId=findSheet.get(0).getId();
        }


        List<List<AddSmartSheetRecordsRequest.Record>> values = Lists.partition(records,updateBatchSize);
        for(List<AddSmartSheetRecordsRequest.Record> value:values){
        AddAiSheetRecordsResponse addAiSheetRecordsResponse = addAiSheetRecords(workbookId, sheetId, value.toArray(new AddAiSheetRecordsRequest.Record[0]));
        }*/

        List<AddAiSheetRecordsRequest.Record> records = importAiExcel(workbookId, valuesData, sheetName, headerValues, updateBatchSize);
        if (StringUtils.isBlank(msgTemplate) || StringUtils.isBlank(endingContent)) {
            String content = msgTitle.concat("\n").concat(java.lang.String.format(ddLinkUrl, workbookId));
            DDUtil.sendMessageWebhook(secret, token, content, atUserIds, atMobiles);
        } else {
            for (List<AddAiSheetRecordsRequest.Record> pv : Lists.partition(records, 9)) {
                String content = msgTitle.concat("\n\n").concat(
                        pv.stream().map(cpv -> String.format(msgTemplate, cpv.getFields().values()).
                                concat("\n")).collect(Collectors.joining("\n")));
                content = content.concat("\n").concat(endingContent);
                content = content.concat("\n").concat(String.format(ddLinkUrl, workbookId));
                DDUtil.sendMessageWebhook(secret, token, content, atUserIds, atMobiles);
            }
        }
    }


//    public static void main(String[] args) {
//        try {
//
//
//            // 2. 准备参数
//            String baseId = "your_base_id_here";
//
//
//            // 3. 构建请求
//            CreateSheetRequest request = new CreateSheetRequest();
//            request.setName("员工信息表");
//
//            // 添加字段配置
//            CreateSheetRequest.FieldConfig nameField = new CreateSheetRequest.FieldConfig();
//            nameField.setName("姓名");
//            nameField.setType("String");
//
//            CreateSheetRequest.FieldConfig ageField = new CreateSheetRequest.FieldConfig();
//            ageField.setName("年龄");
//            ageField.setType("Number");
//
//            request.setFields(Arrays.asList(nameField, ageField));
//
//            // 4. 调用API
//            CreateSheetResponse response = createSheet(baseId, operatorId, request);
//
//            // 5. 处理响应
//            System.out.println("创建成功，数据表ID: " + response.getId());
//            System.out.println("数据表名称: " + response.getName());
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

//    public static void main(String[] args) {
//        try {
//
//
//            // 2. 准备参数
//            String workspaceId = "your_workspace_id_here";
//
//            // 3. 构建请求
//            CreateDocRequest request = new CreateDocRequest();
//            request.setName("项目计划书");
//            request.setDocType("DOC"); // 文档类型：DOC(文字)、WORKBOOK(表格)、MIND(脑图)、FOLDER(文件夹)
//            request.setOperatorId("user_union_id_here");
//            // request.setParentNodeId("parent_node_id_here"); // 可选，不传则在根目录创建
//            // request.setTemplateId("template_id_here"); // 可选
//            // request.setTemplateType("public_template"); // 可选
//
//            // 4. 调用API
//            CreateDocResponse response = createDocument(
//                     workspaceId, request);
//
//            // 5. 处理响应
//            System.out.println("创建成功，文档ID: " + response.getNodeId());
//            System.out.println("文档URL: " + response.getUrl());
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

//    public static void main(String[] args) {
//        try {
//
//
//            // 3. 调用API获取第一页数据
//            WorkspaceListResponse response = getWorkspaceList(
//                    null, // 首次调用不传nextToken
//                    30,   // 每页大小
//                    "VIEW_TIME_DESC", // 按查看时间倒序
//                    true  // 包含权限信息
//            );
//
//            // 4. 处理响应
//            System.out.println("获取到 " + response.getWorkspaces().size() + " 个知识库");
//            response.getWorkspaces().forEach(workspace -> {
//                System.out.println("知识库名称: " + workspace.getName());
//                System.out.println("知识库ID: " + workspace.getWorkspaceId());
//                System.out.println("访问URL: " + workspace.getUrl());
//                System.out.println("权限角色: " + workspace.getPermissionRole());
//                System.out.println("----------------------");
//            });
//

    /// /            // 5. 如果有更多数据，获取下一页
    /// /            if (response.getNextToken() != null && !response.getNextToken().isEmpty()) {
    /// /                System.out.println("获取下一页数据...");
    /// /                WorkspaceListResponse nextPageResponse = getWorkspaceList(
    /// /                        response.getNextToken(),
    /// /                        30,
    /// /                        "VIEW_TIME_DESC",
    /// /                        true
    /// /                );
    /// /
    /// /            }
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

    /**
     * 获取资源上传信息
     */
    public static JSONObject queryUploadInfo(Long size, String mediaType, String resourceName) {
        String url;
        try {
            url = java.lang.String.format(
                    "https://api.dingtalk.com/v1.0/doc/docs/resources/%s/uploadInfos/query?operatorId=%s",
                    URLEncoder.encode(docId, "UTF-8"),
                    URLEncoder.encode(operatorId, "UTF-8")
            );
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(url);
            // 设置请求头
            httpPost.setHeader(tokenKey, getToken());
            httpPost.setHeader("Content-Type", "application/json");
            // 设置请求体
            JSONObject requestJson = new JSONObject();
            requestJson.put("size", size);
            requestJson.put("mediaType", mediaType);
            requestJson.put("resourceName", resourceName);
            String requestBody = objectMapper.writeValueAsString(requestJson);
            httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));
            log.info("钉钉资源上传信息获取入参:{}", requestBody);
            // 执行请求
            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                log.info("钉钉资源上传信息获取结果:{}", responseBody);
                if (response.getStatusLine().getStatusCode() == 200) {
                    JSONObject responseJson = JSON.parseObject(responseBody);
                    return responseJson.getJSONObject("result");
                } else {
                    log.info("钉钉资源上传信息获取请求失败");
                    AiSheetFieldListResponse.ErrorResponse error = objectMapper.readValue(responseBody, AiSheetFieldListResponse.ErrorResponse.class);
                    throw new RuntimeException(String.format("API call failed: %s (code: %s)", error.getMessage(), error.getCode()));
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传文件
     */
    public static void uploadFilesToUrl(String uploadUrl, File file) {
        try {
            String fileName = file.getName();
            URL url = new URL(uploadUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            String mimeType = "";
            if (fileName.endsWith(".pdf")) {
                mimeType = "application/pdf";
            } else if (fileName.endsWith(".xlsx")) {
                mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            } else if (fileName.endsWith(".xls")) {
                mimeType = "application/vnd.ms-excel";
            } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
                mimeType = "image/jpeg";
            }
            connection.setRequestProperty("Content-Type", mimeType);
            connection.setDoOutput(true);
            connection.setRequestMethod("PUT");
            connection.setUseCaches(false);
            connection.setReadTimeout(10000);
            connection.setConnectTimeout(10000);
            connection.connect();
            OutputStream out = connection.getOutputStream();
            InputStream is = Files.newInputStream(file.toPath());
            byte[] b = new byte[1024];
            int temp;
            while ((temp = is.read(b)) != -1) {
                out.write(b, 0, temp);
            }
            out.flush();
            out.close();
            int responseCode = connection.getResponseCode();
            connection.disconnect();
            if (responseCode == 200) {
                System.out.println("上传成功");
            } else {
                System.out.println("上传失败");
            }
        } catch (Exception e) {
            log.error("上传失败:", e);
        }
    }

    /**
     * 查询用户详情
     */
    public static OapiV2UserGetResponse.UserGetResponse queryUserInfo(String userId) {
        OapiV2UserGetResponse.UserGetResponse userGetResponse = null;
        try {
            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
            OapiV2UserGetRequest request = new OapiV2UserGetRequest();
            request.setUserid(userId);
            request.setLanguage("zh_CN");
            log.info("钉钉查询用户详情入参:{}", JSON.toJSONString(request));
            OapiV2UserGetResponse response = client.execute(request, getToken());
            log.info("钉钉查询用户详情返回结果:{}", JSON.toJSONString(response));
            if (response.getErrcode() != 0) {
                throw new RuntimeException(response.getErrmsg());
            }
            userGetResponse = response.getResult();
        } catch (Exception e) {
            log.error("钉钉查询用户详情失败:", e);
        }
        return userGetResponse;
    }

    /**
     * 查询用户详情
     */
    public static String queryLeaveUserInfo(String userId) {
        try {
            String startTime = DateUtil.convertDateToDateStr2(DateUtil.addDays(new Date(), -1));
            startTime = startTime.replace(" ", "T");
            startTime = startTime + "Z";
            com.aliyun.dingtalkcontact_1_0.Client client = DDUtil.createClient();
            ListEmpLeaveRecordsHeaders listEmpLeaveRecordsHeaders = new ListEmpLeaveRecordsHeaders();
            listEmpLeaveRecordsHeaders.xAcsDingtalkAccessToken = getToken();
            ListEmpLeaveRecordsRequest listEmpLeaveRecordsRequest = new ListEmpLeaveRecordsRequest()
                    .setStartTime(startTime)
                    .setNextToken("0")
                    .setMaxResults(50);
            log.info("钉钉查询离职用户入参:{}", JSON.toJSONString(listEmpLeaveRecordsRequest));
            ListEmpLeaveRecordsResponse response = client.listEmpLeaveRecordsWithOptions(listEmpLeaveRecordsRequest, listEmpLeaveRecordsHeaders, new RuntimeOptions());
            log.info("钉钉查询离职用户返回结果:{}", JSON.toJSONString(response));
            List<ListEmpLeaveRecordsResponseBody.ListEmpLeaveRecordsResponseBodyRecords> records = response.getBody().getRecords();
            for (ListEmpLeaveRecordsResponseBody.ListEmpLeaveRecordsResponseBodyRecords record : records) {
                if (userId.equals(record.getUserId())) {
                    return record.getMobile();
                }
            }
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
            log.error("钉钉查询离职用户失败:", err);
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
            log.error("钉钉查询离职用户失败:", _err);
        }
        return null;
    }

    public static com.aliyun.dingtalkcontact_1_0.Client createClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalkcontact_1_0.Client(config);
    }
}
